<!DOCTYPE html>
<html lang=en>
<head>
    <!-- so meta -->
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="HandheldFriendly" content="True">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
    <meta name="description" content="面向对象的3个基本要素：封装、继承、多态 面向对象的5个基本设计原则： 单一职责原则（Single-Resposibility Principle） 其核心思想为： **一个类，最好只做一件事，只有一个引起它的变化。** 单一职责原则可以看做是低耦合、高内聚在面向对象原则上的引申，将职责定义为引起变化的原因，以提高内聚性来减少引起变化的原因。职责过多，可能引起它变化的原因就越多，这将导致职责依赖，">
<meta property="og:type" content="article">
<meta property="og:title" content="面向对象的3个基本要素和5个基本设计原则">
<meta property="og:url" content="http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/index.html">
<meta property="og:site_name" content="TmoonSite">
<meta property="og:description" content="面向对象的3个基本要素：封装、继承、多态 面向对象的5个基本设计原则： 单一职责原则（Single-Resposibility Principle） 其核心思想为： **一个类，最好只做一件事，只有一个引起它的变化。** 单一职责原则可以看做是低耦合、高内聚在面向对象原则上的引申，将职责定义为引起变化的原因，以提高内聚性来减少引起变化的原因。职责过多，可能引起它变化的原因就越多，这将导致职责依赖，">
<meta property="og:locale" content="en_US">
<meta property="article:published_time" content="2015-06-12T11:22:52.000Z">
<meta property="article:modified_time" content="2020-01-07T10:48:14.947Z">
<meta property="article:author" content="Tmoonlight">
<meta name="twitter:card" content="summary">
    
    
        
          
              <link rel="shortcut icon" href="/images/favicon.ico">
          
        
        
          
            <link rel="icon" type="image/png" href="/images/logo2.gif" sizes="192x192">
          
        
        
          
            <link rel="apple-touch-icon" sizes="180x180" href="/images/logo2.gif">
          
        
    
    <!-- title -->
    <title>面向对象的3个基本要素和5个基本设计原则</title>
    <!-- styles -->
    
<link rel="stylesheet" href="/css/style.css">

    <!-- persian styles -->
    
      
<link rel="stylesheet" href="/css/rtl.css">

    
    <!-- rss -->
    
    
<meta name="generator" content="Hexo 4.2.0"></head>

<body class="max-width mx-auto px3 ltr">
    
      <div id="header-post">
  <a id="menu-icon" href="#"><i class="fas fa-bars fa-lg"></i></a>
  <a id="menu-icon-tablet" href="#"><i class="fas fa-bars fa-lg"></i></a>
  <a id="top-icon-tablet" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');" style="display:none;"><i class="fas fa-chevron-up fa-lg"></i></a>
  <span id="menu">
    <span id="nav">
      <ul>
         
          <li><a href="/">Home</a></li>
         
          <li><a href="/about/">About</a></li>
         
          <li><a href="/archives/">Writing</a></li>
         
          <li><a href="/projects_url">Projects</a></li>
        
      </ul>
    </span>
    <br/>
    <span id="actions">
      <ul>
        
        <li><a class="icon" href="/2015/06/15/%E9%80%9F%E8%AE%B0/"><i class="fas fa-chevron-left" aria-hidden="true" onmouseover="$('#i-prev').toggle();" onmouseout="$('#i-prev').toggle();"></i></a></li>
        
        
        <li><a class="icon" href="/2015/06/09/Entityframework%E7%9A%84%E5%9F%BA%E6%9C%AC%E5%BC%80%E5%8F%91%E6%96%B9%E5%BC%8F/"><i class="fas fa-chevron-right" aria-hidden="true" onmouseover="$('#i-next').toggle();" onmouseout="$('#i-next').toggle();"></i></a></li>
        
        <li><a class="icon" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');"><i class="fas fa-chevron-up" aria-hidden="true" onmouseover="$('#i-top').toggle();" onmouseout="$('#i-top').toggle();"></i></a></li>
        <li><a class="icon" href="#"><i class="fas fa-share-alt" aria-hidden="true" onmouseover="$('#i-share').toggle();" onmouseout="$('#i-share').toggle();" onclick="$('#share').toggle();return false;"></i></a></li>
      </ul>
      <span id="i-prev" class="info" style="display:none;">Previous post</span>
      <span id="i-next" class="info" style="display:none;">Next post</span>
      <span id="i-top" class="info" style="display:none;">Back to top</span>
      <span id="i-share" class="info" style="display:none;">Share post</span>
    </span>
    <br/>
    <div id="share" style="display: none">
      <ul>
  <li><a class="icon" href="http://www.facebook.com/sharer.php?u=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/" target="_blank" rel="noopener"><i class="fab fa-facebook " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://twitter.com/share?url=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/&text=面向对象的3个基本要素和5个基本设计原则" target="_blank" rel="noopener"><i class="fab fa-twitter " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://www.linkedin.com/shareArticle?url=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/&title=面向对象的3个基本要素和5个基本设计原则" target="_blank" rel="noopener"><i class="fab fa-linkedin " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://pinterest.com/pin/create/bookmarklet/?url=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/&is_video=false&description=面向对象的3个基本要素和5个基本设计原则" target="_blank" rel="noopener"><i class="fab fa-pinterest " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="mailto:?subject=面向对象的3个基本要素和5个基本设计原则&body=Check out this article: http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/"><i class="fas fa-envelope " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://getpocket.com/save?url=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/&title=面向对象的3个基本要素和5个基本设计原则" target="_blank" rel="noopener"><i class="fab fa-get-pocket " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://reddit.com/submit?url=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/&title=面向对象的3个基本要素和5个基本设计原则" target="_blank" rel="noopener"><i class="fab fa-reddit " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://www.stumbleupon.com/submit?url=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/&title=面向对象的3个基本要素和5个基本设计原则" target="_blank" rel="noopener"><i class="fab fa-stumbleupon " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://digg.com/submit?url=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/&title=面向对象的3个基本要素和5个基本设计原则" target="_blank" rel="noopener"><i class="fab fa-digg " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://www.tumblr.com/share/link?url=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/&name=面向对象的3个基本要素和5个基本设计原则&description=" target="_blank" rel="noopener"><i class="fab fa-tumblr " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://news.ycombinator.com/submitlink?u=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/&t=面向对象的3个基本要素和5个基本设计原则" target="_blank" rel="noopener"><i class="fab fa-hacker-news " aria-hidden="true"></i></a></li>
</ul>

    </div>
    <div id="toc">
      
    </div>
  </span>
</div>

    
    <div class="content index py4">
        
        <article class="post" itemscope itemtype="http://schema.org/BlogPosting">
  <header>
    
    <h1 class="posttitle" itemprop="name headline">
        面向对象的3个基本要素和5个基本设计原则
    </h1>



    <div class="meta">
      <span class="author" itemprop="author" itemscope itemtype="http://schema.org/Person">
        <span itemprop="name">TmoonSite</span>
      </span>
      
    <div class="postdate">
      
        <time datetime="2015-06-12T11:22:52.000Z" itemprop="datePublished">2015-06-12</time>
        
      
    </div>


      

      

    </div>
  </header>
  

  <div class="content" itemprop="articleBody">
    <p>面向对象的3个基本要素：封装、继承、多态</p>
<p>面向对象的5个基本设计原则：</p>
<p>单一职责原则（Single-Resposibility Principle）</p>
<pre><code>其核心思想为： **一个类，最好只做一件事，只有一个引起它的变化。** 单一职责原则可以看做是低耦合、高内聚在面向对象原则上的引申，将职责定义为引起变化的原因，以提高内聚性来减少引起变化的原因。职责过多，可能引起它变化的原因就越多，这将导致职责依赖，相互之间就产生影响，从而大大损伤其内聚性和耦合度。通常意义下的单一职责，就是指只有一种单一功能，不要为类实现过多的功能点，以保证实体只有一个引起它变化的原因。

专注，是一个人优良的品质；同样的，单一也是一个类的优良设计。交杂不清的职责将使得代码看起来特别别扭牵一发而动全身，有失美感和必然导致丑陋的系统错误风险。</code></pre><p>开放封闭原则（Open-Closed principle）</p>
<pre><code>其核心思想是：软件实体应该是可扩展的，而不可修改的。也就是， **对扩展开放，对修改封闭的。** 开放封闭原则主要体现在两个方面1、对扩展开放，意味着有新的需求或变化时，可以对现有代码进行扩展，以适应新的情况。2、对修改封闭，意味着类一旦设计完成，就可以独立完成其工作，而不要对其进行任何尝试的修改。

实现开开放封闭原则的核心思想就是对抽象编程，而不对具体编程，因为抽象相对稳定。让类依赖于固定的抽象，所以修改就是封闭的；而通过面向对象的继承和多态机制，又可以实现对抽象类的继承，通过覆写其方法来改变固有行为，实现新的拓展方法，所以就是开放的。

“需求总是变化”没有不变的软件，所以就需要用封闭开放原则来封闭变化满足需求，同时还能保持软件内部的封装体系稳定，不被需求的变化影响。</code></pre><p>Liskov替换原则（Liskov-Substituion Principle）</p>
<pre><code>其核心思想是： **子类必须能够替换其基类。** 这一思想体现为对继承机制的约束规范，只有子类能够替换基类时，才能保证系统在运行期内识别子类，这是保证继承复用的基础。在父类和子类的具体行为中，必须严格把握继承层次中的关系和特征，将基类替换为子类，程序的行为不会发生任何变化。同时，这一约束反过来则是不成立的，子类可以替换基类，但是基类不一定能替换子类。

Liskov替换原则，主要着眼于对抽象和多态建立在继承的基础上，因此只有遵循了Liskov替换原则，才能保证继承复用是可靠地。实现的方法是面向接口编程：将公共部分抽象为基类接口或抽象类，通过Extract Abstract Class，在子类中通过覆写父类的方法实现新的方式支持同样的职责。

Liskov替换原则是关于继承机制的设计原则，违反了Liskov替换原则就必然导致违反开放封闭原则。

Liskov替换原则能够保证系统具有良好的拓展性，同时实现基于多态的抽象机制，能够减少代码冗余，避免运行期的类型判别。</code></pre><p>依赖倒置原则（Dependecy-Inversion Principle）</p>
<pre><code>其核心思想是： **依赖于抽象。** 具体而言就是高层模块不依赖于底层模块，二者都同依赖于抽象；抽象不依赖于具体，具体依赖于抽象。

我们知道，依赖一定会存在于类与类、模块与模块之间。当两个模块之间存在紧密的耦合关系时，最好的方法就是分离接口和实现：在依赖之间定义一个抽象的接口使得高层模块调用接口，而底层模块实现接口的定义，以此来有效控制耦合关系，达到依赖于抽象的设计目标。

抽象的稳定性决定了系统的稳定性，因为抽象是不变的，依赖于抽象是面向对象设计的精髓，也是依赖倒置原则的核心。

依赖于抽象是一个通用的原则，而某些时候依赖于细节则是在所难免的，必须权衡在抽象和具体之间的取舍，方法不是一层不变的。依赖于抽象，就是对接口编程，不要对实现编程。</code></pre><p>接口隔离原则（Interface-Segregation Principle）</p>
<pre><code>其核心思想是： **使用多个小的专门的接口，而不要使用一个大的总接口。**

具体而言，接口隔离原则体现在：接口应该是内聚的，应该避免“胖”接口。一个类对另外一个类的依赖应该建立在最小的接口上，不要强迫依赖不用的方法，这是一种接口污染。

接口有效地将细节和抽象隔离，体现了对抽象编程的一切好处，接口隔离强调接口的单一性。而胖接口存在明显的弊端，会导致实现的类型必须完全实现接口的所有方法、属性等；而某些时候，实现类型并非需要所有的接口定义，在设计上这是“浪费”，而且在实施上这会带来潜在的问题，对胖接口的修改将导致一连串的客户端程序需要修改，有时候这是一种灾难。在这种情况下，将胖接口分解为多个特点的定制化方法，使得客户端仅仅依赖于它们的实际调用的方法，从而解除了客户端不会依赖于它们不用的方法。

分离的手段主要有以下两种：1、委托分离，通过增加一个新的类型来委托客户的请求，隔离客户和接口的直接依赖，但是会增加系统的开销。2、多重继承分离，通过接口多继承来实现客户的需求，这种方式是较好的。</code></pre><p>下面进行一一详细介绍。</p>
<p>一、SRP简介（SRP–Single-Responsibility Principle）：</p>
<p>就一个类而言，应该只专注于做一件事和仅有一个引起它变化的原因。</p>
<p>所谓职责，我们可以理解他为功能，就是设计的这个类功能应该只有一个，而不是两个或更多。也可以理解为引用变化的原因，当你发现有两个变化会要求我们修改这个类，那么你就要考虑撤分这个类了。因为职责是变化的一个轴线，当需求变化时，该变化会反映类的职责的变化。</p>
<p>“就像一个人身兼数职，而这些事情相互关联不大，，甚至有冲突，那他就无法很好的解决这些职责，应该分到不同的人身上去做才对。”</p>
<p>二、举例说明：</p>
<p>违反SRP原则代码:</p>
<p>modem接口明显具有两个职责：连接管理和数据通讯；</p>
<p>Java代码</p>
<ol>
<li><strong>interface</strong>  Modem</li>
<li>{</li>
<li><strong>public</strong>   <strong>void</strong>  dial(string pno);</li>
<li><strong>public</strong>   <strong>void</strong>  hangup();</li>
<li><strong>public</strong>   <strong>void</strong>  send( <strong>char</strong>  c);</li>
<li><strong>public</strong>   <strong>void</strong>  recv();</li>
<li>}</li>
</ol>
<p>如果应用程序变化影响连接函数，那么就需要重构：</p>
<p>Java代码</p>
<ol>
<li><strong>interface</strong>  DataChannel</li>
<li>{</li>
<li><strong>public</strong>   <strong>void</strong>  send( <strong>char</strong>  c);</li>
<li><strong>public</strong>   <strong>void</strong>  recv();</li>
<li>}</li>
<li><strong>interface</strong>  Connection</li>
<li>{</li>
<li><strong>public</strong>   <strong>void</strong>  dial(string pno);</li>
<li><strong>public</strong>   <strong>void</strong>  hangup();</li>
<li>}</li>
</ol>
<p>三、SRP优点：</p>
<p>消除耦合，减小因需求变化引起代码僵化性臭味</p>
<p>四、使用SRP注意点：</p>
<p>1、一个合理的类，应该仅有一个引起它变化的原因，即单一职责；</p>
<p>2、在没有变化征兆的情况下应用SRP或其他原则是不明智的；</p>
<p>3、在需求实际发生变化时就应该应用SRP等原则来重构代码；</p>
<p>4、使用测试驱动开发会迫使我们在设计出现臭味之前分离不合理代码；</p>
<p>5、如果测试不能迫使职责分离，僵化性和脆弱性的臭味会变得很强烈，那就应该用Facade或Proxy模式对代码重构；</p>
<p>一、OCP简介（OCP–Open-Closed Principle）：</p>
<p>Software entities(classes,modules,functions,etc.) should be open for extension, but closed for modification。</p>
<p>软件实体应当对扩展开放，对修改关闭，即软件实体应当在不修改（在.Net当中可能通过代理模式来达到这个目的）的前提下扩展。</p>
<p>Open for extension:当新需求出现的时候，可以通过扩展现有模型达到目的。</p>
<p>Close for modification:对已有的二进制代码，如dll,jar等，则不允许做任何修改。</p>
<p>二、OCP举例：</p>
<p>1、例子一</p>
<p>假如我们要写一个工资税类，工资税在不同国家有不同计算规则，如果我们不坚持OCP，直接写一个类封装工资税的算税方法，而每个国家对工资税的具体实现细节是不尽相同的！如果我们允许修改，即把现在系统需要的所有工资税（中国工资税、美国工资税等）都放在一个类里实现，谁也不能保证未来系统不会被卖到日本，一旦出现新的工资税，而在软件中必须要实现这种工资税，这个时候我们能做的只有找出这个类文件，在每个方法里加上日本税的实现细节并重新编译成DLL！虽然在.NET的运行环境中，我们只要将新的DLL覆盖到原有的DLL即可，并不影响现有程序的正常运行，但每次出现新情况都要找出类文件，添加新的实现细节，这个类文件不断扩大，以后维护起来就变的越来越困难，也并不满足我们以前说的单一职责原则（SRP），因为不同国家的工资税变化都会引起对这个类的改变动机！如果我们在设计这个类的时候坚持了OCP的话，把工资税的公共方法抽象出来做成一个接口，封闭修改，在客户端(使用该接口的类对象)只依赖这个接口来实现对自己所需要的工资税，以后如果系统需要增加新的工资税，只要扩展一个具体国家的工资税实现我们先前定义的接口，就可以正常使用，而不必重新修改原有类文件！</p>
<p>2、例子二</p>
<p>下面这个例子就是既不开放也不封闭的，因为Client和Server都是具体类，如果我要Client使用不同的一个Server类那就要修改Client类中所有使用Server类的地方为新的Server类。</p>
<p>Java代码</p>
<ol>
<li><strong>class</strong>  Client</li>
<li>{</li>
<li>Server server;</li>
<li><strong>void</strong>  GetMessage()</li>
<li>{</li>
<li>server.Message();</li>
<li>}</li>
<li>}</li>
<li><strong>class</strong>  Server</li>
<li>{</li>
<li><strong>void</strong>  Message();</li>
<li>}</li>
</ol>
<p>下面为修改后符合OCP原则的实现，我们看到Server类是从ClientInterface继承的，不过ClientInterface却不叫ServerInterface，原因是我们希望对Client来说ClientInterface是固定下来的，变化的只是Server。这实际上就变成了一种策略模式(Gof Strategy）</p>
<p>Java代码</p>
<ol>
<li><strong>interface</strong>  ClientInterface</li>
<li>{</li>
<li><strong>public</strong>   <strong>void</strong>  Message();</li>
<li>//Other functions</li>
<li>}</li>
<li><strong>class</strong>  Server:ClientInterface</li>
<li>{</li>
<li><strong>public</strong>   <strong>void</strong>  Message();</li>
<li>}</li>
<li><strong>class</strong>  Client</li>
<li>{</li>
<li>ClientInterface ci;</li>
<li><strong>public</strong>   <strong>void</strong>  GetMessage()</li>
<li>{</li>
<li>ci.Message();</li>
<li>}</li>
<li><strong>public</strong>   <strong>void</strong>  Client(ClientInterface paramCi)</li>
<li>{</li>
<li>ci=paramCi;</li>
<li>}</li>
<li>}</li>
<li>//那么在主函数(或主控端)则</li>
<li><strong>public</strong>   <strong>static</strong>   <strong>void</strong>  Main()</li>
<li>{</li>
<li>ClientInterface ci = <strong>new</strong>  Server();</li>
<li>//在上面如果有新的Server类只要替换Server()就行了．</li>
<li>Client client = <strong>new</strong>  Client(ci);</li>
<li>client.GetMessage();</li>
<li>}</li>
</ol>
<p>3、例子三</p>
<p>使用Template Method实现OCP：</p>
<p>Java代码</p>
<ol>
<li><strong>public</strong>   <strong>abstract</strong>   <strong>class</strong>  Policy</li>
<li>{</li>
<li><strong>private</strong>   <strong>int</strong> [] i ={ 1, 1234, 1234, 1234, 132 };</li>
<li><strong>public</strong>  bool Sort()</li>
<li>{</li>
<li>SortImp();</li>
<li>}</li>
<li><strong>protected</strong>  virtual bool SortImp()</li>
<li>{</li>
<li>}</li>
<li>}</li>
<li><strong>class</strong>  Bubbleimp : Policy</li>
<li>{</li>
<li><strong>protected</strong>  override bool SortImp()</li>
<li>{</li>
<li>//冒泡排序</li>
<li>}</li>
<li>}</li>
<li><strong>class</strong>  Bintreeimp : Policy</li>
<li>{</li>
<li><strong>protected</strong>  override bool SortImp()</li>
<li>{</li>
<li>//二分法排序</li>
<li>}</li>
<li>}</li>
<li>//主函数中实现</li>
<li><strong>static</strong>   <strong>void</strong>  Main(string[] args)</li>
<li>{</li>
<li>//如果要使用冒泡排序，只要把下面的Bintreeimp改为Bubbleimp</li>
<li>Policy sort = <strong>new</strong>  Bintreeimp();</li>
<li>sort.Sort();</li>
<li>}</li>
</ol>
<p>三、OCP优点：</p>
<p>1、降低程序各部分之间的耦合性，使程序模块互换成为可能；</p>
<p>2、使软件各部分便于单元测试，通过编制与接口一致的模拟类（Mock），可以很容易地实现软件各部分的单元测试；</p>
<p>3、利于实现软件的模块的呼唤，软件升级时可以只部署发生变化的部分，而不会影响其它部分；</p>
<p>四、使用OCP注意点：</p>
<p>1、实现OCP原则的关键是抽象；</p>
<p>2、两种安全的实现开闭原则的设计模式是：Strategy pattern（策略模式），Template Methord（模版方法模式）；</p>
<p>3、依据开闭原则,我们尽量不要修改类,只扩展类,但在有些情况下会出现一些比较怪异的状况，这时可以采用几个类进行组合来完成；</p>
<p>4、将可能发生变化的部分封装成一个对象,如: 状态, 消息,,算法,数据结构等等 , 封装变化是实现”开闭原则”的一个重要手段，如经常发生变化的状态值,如温度,气压,颜色,积分,排名等等,可以将这些作为独立的属性,如果参数之间有关系,有必要进行抽象。对于行为,如果是基本不变的,则可以直接作为对象的方法,否则考虑抽象或者封装这些行为；</p>
<p>5、在许多方面，OCP是面向对象设计的核心所在。遵循这个原则可带来面向对象技术所声称的巨大好处（灵活性、可重用性以及可维护性）。然而，对于应用程序的每个部分都肆意地进行抽象并不是一个好主意。应该仅仅对程序中呈现出频繁变化的那部分作出抽象。拒绝不成熟的抽象和抽象本身一样重要；</p>
<p>一、LSP简介（LSP–Liskov Substitution Principle）：</p>
<p>定义：如果对于类型S的每一个对象o1，都有一个类型T的对象o2，使对于任意用类型T定义的程序P，将o2替换为o1，P的行为保持不变，则称S为T的一个子类型。</p>
<p>子类型必须能够替换它的基类型。LSP又称里氏替换原则。</p>
<p>对于这个原则，通俗一些的理解就是，父类的方法都要在子类中实现或者重写。</p>
<p>二、举例说明：</p>
<p>对于依赖倒置原则，说的是父类不能依赖子类，它们都要依赖抽象类。这种依赖是我们实现代码扩展和运行期内绑定（多态）的基础。因为一旦类的使用者依赖某个具体的类，那么对该依赖的扩展就无从谈起；而依赖某个抽象类，则只要实现了该抽象类的子类，都可以被类的使用者使用，从而实现了系统的扩展。</p>
<p>但是，光有依赖倒置原则，并不一定就使我们的代码真正具有良好的扩展性和运行期内绑定。请看下面的代码：</p>
<p>Java代码</p>
<ol>
<li><strong>public</strong>   <strong>class</strong>  Animal</li>
<li>{</li>
<li><strong>private</strong>  string name;</li>
<li><strong>public</strong>  Animal(string name)</li>
<li>{</li>
<li><strong>this</strong>.name = name;</li>
<li>}</li>
<li><strong>public</strong>   <strong>void</strong>  Description()</li>
<li>{</li>
<li>Console.WriteLine(“This is a(an) “ + name);</li>
<li>}</li>
<li>}</li>
<li>//下面是它的子类猫类：</li>
<li><strong>public</strong>   <strong>class</strong>  Cat : Animal</li>
<li>{</li>
<li><strong>public</strong>  Cat(string name)</li>
<li>{</li>
<li>}</li>
<li><strong>public</strong>   <strong>void</strong>  Mew()</li>
<li>{</li>
<li>Console.WriteLine(“The cat is saying like ‘mew’”);</li>
<li>}</li>
<li>}</li>
<li>//下面是它的子类狗类：</li>
<li><strong>public</strong>   <strong>class</strong>  Dog : Animal</li>
<li>{</li>
<li><strong>public</strong>  Dog(string name)</li>
<li>{</li>
<li>}</li>
<li><strong>public</strong>   <strong>void</strong>  Bark()</li>
<li>{</li>
<li>Console.WriteLine(“The dog is saying like ‘bark’”);</li>
<li>}</li>
<li>}</li>
<li>//最后，我们来看客户端的调用：</li>
<li><strong>public</strong>   <strong>void</strong>  DecriptionTheAnimal(Animal animal)</li>
<li>{</li>
<li><strong>if</strong>  (typeof(animal) is Cat)</li>
<li>{</li>
<li>Cat cat = (Cat)animal;</li>
<li>Cat.Decription();</li>
<li>Cat.Mew();</li>
<li>}</li>
<li><strong>else</strong>   <strong>if</strong>  (typeof(animal) is Dog)</li>
<li>{</li>
<li>Dog dog = (Dog)animal;</li>
<li>Dog.Decription();</li>
<li>Dog.Bark();</li>
<li>}</li>
<li>}</li>
</ol>
<p>通过上面的代码，我们可以看到虽然客户端的依赖是对抽象的依赖，但依然这个设计的扩展性不好，运行期绑定没有实现。</p>
<p>是什么原因呢？其实就是因为不满足里氏替换原则，子类如Cat有Mew()方法父类根本没有，Dog类有Bark()方法父类也没有，两个子类都不能替换父类。这样导致了系统的扩展性不好和没有实现运行期内绑定。</p>
<p>现在看来，一个系统或子系统要拥有良好的扩展性和实现运行期内绑定，有两个必要条件：第一是依赖倒置原则；第二是里氏替换原则。这两个原则缺一不可。</p>
<p>我们知道，在我们的大多数的模式中，我们都有一个共同的接口，然后子类和扩展类都去实现该接口。</p>
<p>下面是一段原始代码：</p>
<p>if(action.Equals(“add”))</p>
<p>Java代码</p>
<ol>
<li>{</li>
<li>//do add action</li>
<li>}</li>
<li><strong>else</strong>   <strong>if</strong> (action.Equals(“view”))</li>
<li>{</li>
<li>//do view action</li>
<li>}</li>
<li><strong>else</strong>   <strong>if</strong> (action.Equals(“delete”))</li>
<li>{</li>
<li>//do delete action</li>
<li>}</li>
<li><strong>else</strong>   <strong>if</strong> (action.Equals(“modify”))</li>
<li>{</li>
<li>//do modify action</li>
<li>}</li>
</ol>
<p>我们首先想到的是把这些动作分离出来，就可能写出如下的代码：</p>
<p>Java代码</p>
<ol>
<li><strong>public</strong>   <strong>class</strong>  AddAction</li>
<li>{</li>
<li><strong>public</strong>   <strong>void</strong>  add()</li>
<li>{</li>
<li>//do add action</li>
<li>}</li>
<li>}</li>
<li><strong>public</strong>   <strong>class</strong>  ViewAction</li>
<li>{</li>
<li><strong>public</strong>   <strong>void</strong>  view()</li>
<li>{</li>
<li>//do view action</li>
<li>}</li>
<li>}</li>
<li><strong>public</strong>   <strong>class</strong>  deleteAction</li>
<li>{</li>
<li><strong>public</strong>   <strong>void</strong>  delete()</li>
<li>{</li>
<li>//do delete action</li>
<li>}</li>
<li>}</li>
<li><strong>public</strong>   <strong>class</strong>  ModifyAction</li>
<li>{</li>
<li><strong>public</strong>   <strong>void</strong>  modify()</li>
<li>{</li>
<li>//do modify action</li>
<li>}</li>
<li>}</li>
</ol>
<p>我们可以看到，这样代码将各个行为独立出来，满足了单一职责原则，但这远远不够，因为它不满足依赖颠倒原则和里氏替换原则。</p>
<p>下面我们来看看命令模式对该问题的解决方法：</p>
<p>Java代码</p>
<ol>
<li><strong>public</strong>   <strong>interface</strong>  Action</li>
<li>{</li>
<li><strong>public</strong>   <strong>void</strong>  doAction();</li>
<li>}</li>
<li>//然后是各个实现：</li>
<li><strong>public</strong>   <strong>class</strong>  AddAction : Action</li>
<li>{</li>
<li><strong>public</strong>   <strong>void</strong>  doAction()</li>
<li>{</li>
<li>//do add action</li>
<li>}</li>
<li>}</li>
<li><strong>public</strong>   <strong>class</strong>  ViewAction : Action</li>
<li>{</li>
<li><strong>public</strong>   <strong>void</strong>  doAction()</li>
<li>{</li>
<li>//do view action</li>
<li>}</li>
<li>}</li>
<li><strong>public</strong>   <strong>class</strong>  deleteAction : Action</li>
<li>{</li>
<li><strong>public</strong>   <strong>void</strong>  doAction()</li>
<li>{</li>
<li>//do delete action</li>
<li>}</li>
<li>}</li>
<li><strong>public</strong>   <strong>class</strong>  ModifyAction : Action</li>
<li>{</li>
<li><strong>public</strong>   <strong>void</strong>  doAction()</li>
<li>{</li>
<li>//do modify action</li>
<li>}</li>
<li>}</li>
<li>//这样，客户端的调用大概如下：</li>
<li><strong>public</strong>   <strong>void</strong>  execute(Action action)</li>
<li>{</li>
<li>action.doAction();</li>
<li>}</li>
</ol>
<p>看，上面的客户端代码再也没有出现过typeof这样的语句，扩展性良好，也有了运行期内绑定的优点。</p>
<p>三、LSP优点：</p>
<p>1、保证系统或子系统有良好的扩展性。只有子类能够完全替换父类，才能保证系统或子系统在运行期内识别子类就可以了，因而使得系统或子系统有了良好的扩展性。</p>
<p>2、实现运行期内绑定，即保证了面向对象多态性的顺利进行。这节省了大量的代码重复或冗余。避免了类似instanceof这样的语句，或者getClass()这样的语句，这些语句是面向对象所忌讳的。</p>
<p>3、有利于实现契约式编程。契约式编程有利于系统的分析和设计，指我们在分析和设计的时候，定义好系统的接口，然后再编码的时候实现这些接口即可。在父类里定义好子类需要实现的功能，而子类只要实现这些功能即可。</p>
<p>四、使用LSP注意点：</p>
<p>1、此原则和OCP的作用有点类似，其实这些面向对象的基本原则就2条：1：面向接口编程，而不是面向实现；2：用组合而不主张用继承</p>
<p>2、LSP是保证OCP的重要原则</p>
<p>3、这些基本的原则在实现方法上也有个共同层次，就是使用中间接口层，以此来达到类对象的低偶合，也就是抽象偶合！</p>
<p>4、派生类的退化函数：派生类的某些函数退化（变得没有用处），Base的使用者不知道不能调用f，会导致替换违规。在派生类中存在退化函数并不总是表示违反了LSP，但是当存在这种情况时，应该引起注意。</p>
<p>5、从派生类抛出异常：如果在派生类的方法中添加了其基类不会抛出的异常。如果基类的使用者不期望这些异常，那么把他们添加到派生类的方法中就可以能会导致不可替换性。</p>
<p>一、DIP简介（DIP–Dependency Inversion Principle）：</p>
<p>1、高层模块不应该依赖于低层模块,二者都应该依赖于抽象。</p>
<p>2、抽象不应该依赖于细节,细节应该依赖于抽象。</p>
<p>高层模块包含了一个应该程序中的重要的策略选择和业务模型，正是这些高层模块才使得其所有的应用程序区别于其他，如果高层依赖于低层,那么对低层模块的改动就会直接影响到高层模块,从而迫使它们依次做出改动。</p>
<p>二、举例说明：</p>
<p>反面例子：</p>
<p>缺点：耦合太紧密，Light发生变化将影响ToggleSwitch。</p>
<p>解决办法一：</p>
<p>将Light作成Abstract，然后具体类继承自Light。</p>
<p>优点：ToggleSwitch依赖于抽象类Light，具有更高的稳定性，而BulbLight与TubeLight继承自Light，可以根据”开放－封闭”原则进行扩展。只要Light不发生变化，BulbLight与TubeLight的变化就不会波及ToggleSwitch。</p>
<p>缺点：如果用ToggleSwitch控制一台电视就很困难了。总不能让TV继承自Light吧。</p>
<p>解决方法二：</p>
<p>优点：更为通用、更为稳定。</p>
<p>三、DIP优点：</p>
<p>使用传统过程化程序设计所创建的依赖关系，策略依赖于细节，这是糟糕的，因为策略受到细节改变的影响。依赖倒置原则使细节和策略都依赖于抽象，抽象的稳定性决定了系统的稳定性。</p>
<p>四、启发式规则:</p>
<p>1、任何变量都不应该持有一个指向具体类的指针或者引用</p>
<p>2、任何类都不应该从具体类派生(始于抽象,来自具体)</p>
<p>3、任何方法都不应该覆写它的任何基类中的已经实现了的方法</p>
<p>一、ISP简介（ISP–Interface Segregation Principle）：</p>
<p>使用多个专门的接口比使用单一的总接口要好。</p>
<p>一个类对另外一个类的依赖性应当是建立在最小的接口上的。</p>
<p>一个接口代表一个角色，不应当将不同的角色都交给一个接口。没有关系的接口合并在一起，形成一个臃肿的大接口，这是对角色和接口的污染。</p>
<p>“不应该强迫客户依赖于它们不用的方法。接口属于客户，不属于它所在的类层次结构。”这个说得很明白了，再通俗点说，不要强迫客户使用它们不用的方法，如果强迫用户使用它们不使用的方法，那么这些客户就会面临由于这些不使用的方法的改变所带来的改变。</p>
<p>二、举例说明：</p>
<p>参考下图的设计，在这个设计里，取款、存款、转帐都使用一个通用界面接口，也就是说，每一个类都被强迫依赖了另两个类的接口方法，那么每个类有可能因为另外两个类的方法(跟自己无关)而被影响。拿取款来说，它根本不关心“存款操作”和“转帐操作”，可是它却要受到这两个方法的变化的影响。</p>
<p>那么我们该如何解决这个问题呢？参考下图的设计，为每个类都单独设计专门的操作接口，使得它们只依赖于它们关系的方法，这样就不会互相影了！</p>
<p>三、实现方法：</p>
<p>1、使用委托分离接口</p>
<p>2、使用多重继承分离接口</p>
<pre><code>以上就是5个基本的面向对象设计原则，它们就像面向对象程序设计中的金科玉律，遵守它们可以使我们的代码更加鲜活，易于复用，易于拓展，灵活优雅。不同的设计模式对应不同的需求，而设计原则则代表永恒的灵魂，需要在实践中时时刻刻地遵守。就如ARTHUR J.RIEL在那边《OOD启示录》中所说的：“你并不必严格遵守这些原则，违背它们也不会被处以宗教刑罚。但你应当把这些原则看做警铃，若违背了其中的一条，那么警铃就会响起。”</code></pre><p>面向对象设计原则 <a href="http://www.cnblogs.com/feipeng/archive/2007/03/02/661840.html" target="_blank" rel="noopener">http://www.cnblogs.com/feipeng/archive/2007/03/02/661840.html</a></p>

  </div>
</article>



        
          <div id="footer-post-container">
  <div id="footer-post">

    <div id="nav-footer" style="display: none">
      <ul>
         
          <li><a href="/">Home</a></li>
         
          <li><a href="/about/">About</a></li>
         
          <li><a href="/archives/">Writing</a></li>
         
          <li><a href="/projects_url">Projects</a></li>
        
      </ul>
    </div>

    <div id="toc-footer" style="display: none">
      
    </div>

    <div id="share-footer" style="display: none">
      <ul>
  <li><a class="icon" href="http://www.facebook.com/sharer.php?u=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/" target="_blank" rel="noopener"><i class="fab fa-facebook fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://twitter.com/share?url=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/&text=面向对象的3个基本要素和5个基本设计原则" target="_blank" rel="noopener"><i class="fab fa-twitter fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://www.linkedin.com/shareArticle?url=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/&title=面向对象的3个基本要素和5个基本设计原则" target="_blank" rel="noopener"><i class="fab fa-linkedin fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://pinterest.com/pin/create/bookmarklet/?url=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/&is_video=false&description=面向对象的3个基本要素和5个基本设计原则" target="_blank" rel="noopener"><i class="fab fa-pinterest fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="mailto:?subject=面向对象的3个基本要素和5个基本设计原则&body=Check out this article: http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/"><i class="fas fa-envelope fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://getpocket.com/save?url=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/&title=面向对象的3个基本要素和5个基本设计原则" target="_blank" rel="noopener"><i class="fab fa-get-pocket fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://reddit.com/submit?url=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/&title=面向对象的3个基本要素和5个基本设计原则" target="_blank" rel="noopener"><i class="fab fa-reddit fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://www.stumbleupon.com/submit?url=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/&title=面向对象的3个基本要素和5个基本设计原则" target="_blank" rel="noopener"><i class="fab fa-stumbleupon fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://digg.com/submit?url=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/&title=面向对象的3个基本要素和5个基本设计原则" target="_blank" rel="noopener"><i class="fab fa-digg fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="http://www.tumblr.com/share/link?url=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/&name=面向对象的3个基本要素和5个基本设计原则&description=" target="_blank" rel="noopener"><i class="fab fa-tumblr fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="https://news.ycombinator.com/submitlink?u=http://yoursite.com/2015/06/12/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%843%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%A6%81%E7%B4%A0%E5%92%8C5%E4%B8%AA%E5%9F%BA%E6%9C%AC%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/&t=面向对象的3个基本要素和5个基本设计原则" target="_blank" rel="noopener"><i class="fab fa-hacker-news fa-lg" aria-hidden="true"></i></a></li>
</ul>

    </div>

    <div id="actions-footer">
        <a id="menu" class="icon" href="#" onclick="$('#nav-footer').toggle();return false;"><i class="fas fa-bars fa-lg" aria-hidden="true"></i> Menu</a>
        <a id="toc" class="icon" href="#" onclick="$('#toc-footer').toggle();return false;"><i class="fas fa-list fa-lg" aria-hidden="true"></i> TOC</a>
        <a id="share" class="icon" href="#" onclick="$('#share-footer').toggle();return false;"><i class="fas fa-share-alt fa-lg" aria-hidden="true"></i> Share</a>
        <a id="top" style="display:none" class="icon" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');"><i class="fas fa-chevron-up fa-lg" aria-hidden="true"></i> Top</a>
    </div>

  </div>
</div>

        
        <footer id="footer">
  <div class="footer-left">
    Copyright &copy; 2020 Tmoonlight
  </div>
  <div class="footer-right">
    <nav>
      <ul>
         
          <li><a href="/">Home</a></li>
         
          <li><a href="/about/">About</a></li>
         
          <li><a href="/archives/">Writing</a></li>
         
          <li><a href="/projects_url">Projects</a></li>
        
      </ul>
    </nav>
  </div>
</footer>

    </div>
    <!-- styles -->

<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">


<link rel="stylesheet" href="/lib/justified-gallery/css/justifiedGallery.min.css">


    <!-- jquery -->

<script src="/lib/jquery/jquery.min.js"></script>


<script src="/lib/justified-gallery/js/jquery.justifiedGallery.min.js"></script>

<!-- clipboard -->

  
<script src="/lib/clipboard/clipboard.min.js"></script>

  <script type="text/javascript">
  $(function() {
    // copy-btn HTML
    var btn = "<span class=\"btn-copy tooltipped tooltipped-sw\" aria-label=\"Copy to clipboard!\">";
    btn += '<i class="far fa-clone"></i>';
    btn += '</span>'; 
    // mount it!
    $(".highlight table").before(btn);
    var clip = new ClipboardJS('.btn-copy', {
      text: function(trigger) {
        return Array.from(trigger.nextElementSibling.querySelectorAll('.code')).reduce((str,it)=>str+it.innerText+'\n','')
      }
    });
    clip.on('success', function(e) {
      e.trigger.setAttribute('aria-label', "Copied!");
      e.clearSelection();
    })
  })
  </script>


<script src="/js/main.js"></script>

<!-- search -->

<!-- Google Analytics -->

<!-- Baidu Analytics -->

<!-- Disqus Comments -->


</body>
</html>
